package com.rengwuxian.coursecoroutines._4_flow

import com.rengwuxian.coursecoroutines.common.gitHub
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.channels.produce
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.EmptyCoroutineContext

/**
 * 用produce()来提供跨协程的事件流
 * Channel、produce()、ReceiveChannel、SendChannel
 *
 * Channel：是协程间通信的关键点
 * 日常开发的角度，几乎用不上，但是基础架构的角色是必要的。
 * 想弄清楚Flow，必须要知道Channel
 *
 * ProduceScope就是当作CoroutineScope一样来用，只不过多了一个SendChannel的功能，给别的协程发送数据。send(data)
 * produce()的返回值，是ReceiveChannel，可以在别的协程拿到它来接收数据。
 * 每次的receive()对应的都是不同的send()数据，所以能每次拿到的都是不一样的。
 *
 * 本例是使用Channel来持续发送网络请求数据，在另一个协程中不停地拿到新数据展示。
 * 输出如下：
 * Contributors: [Contributor(login=Proposition Joe, contributions=39), Contributor(login=Keiser Soze, contributions=152)]
 * --------------------------
 * produce delay start
 * produce delay end
 * Contributors: [Contributor(login=John Doe, contributions=12), Contributor(login=Bob Smith, contributions=2), Contributor(login=Big Bird, contributions=40)]
 * Contributors: [Contributor(login=John Doe, contributions=12), Contributor(login=Bob Smith, contributions=2), Contributor(login=Big Bird, contributions=40)]
 * ......
 *
 * 扩充知识：SSE:Server-Sent Events 服务器发送事件
 * scope.produce {} 也可以启动协程，是一个有特定用途的协程，给别的协程发送消息的协程。
 * 经过实验：produce产生的数据，被消费完就没有了，如果没有生产就消费，就会抛出：Channel was closed的异常
 *
 */
@OptIn(ExperimentalCoroutinesApi::class) //试验性API，但是当前功能完全没有问题，后续设计方向可能会变动。
fun main() = runBlocking<Unit> {
  val scopeT = CoroutineScope(EmptyCoroutineContext)
  val deferred = scopeT.async { gitHub.contributors("square", "okhttp") }
  delay(5000)
  println("Contributors: ${deferred.await()}")
  println("--------------------------")

  val scope = CoroutineScope(EmptyCoroutineContext)
  val receiver = scope.produce {
    println("produce delay start")
    delay(5000)
    println("produce delay end")
    while (isActive) {
      val data = gitHub.contributors("square", "retrofit")
      send(data)
    }
  }
  launch {
    delay(5000)
    while (isActive) {
      println("Contributors: ${receiver.receive()}")
    }
  }
  delay(10000)
}